
#include "sqlite3.h"
#include "sqlite3x.hpp"

namespace sqlite3x {

    sqlite3_reader::sqlite3_reader() : cmd(NULL) {}

    sqlite3_reader::sqlite3_reader(const sqlite3_reader &copy) :
    cmd(copy.cmd),
        colnames(copy.colnames)
    {
        if(this->cmd) ++this->cmd->refs;
    }

    sqlite3_reader::sqlite3_reader(sqlite3_command *cmd) :
    cmd(cmd),
        colnames(new colname_index_t)
    {
        ++cmd->refs;
    }

    sqlite3_reader::~sqlite3_reader() {
        this->close();
    }

    sqlite3_reader& sqlite3_reader::operator=(const sqlite3_reader &copy) {
        this->close();

        this->cmd=copy.cmd;
        if(this->cmd) ++this->cmd->refs;

        return *this;
    }

    bool sqlite3_reader::read() {
        if(!this->cmd) throw database_error("reader is closed");

        switch(sqlite3_step(this->cmd->stmt)) {
        case SQLITE_ROW:
            return true;
        case SQLITE_DONE:
            return false;
        default:
            throw database_error(this->cmd->con);
        }
    }

    void sqlite3_reader::reset() {
        if(!this->cmd) throw database_error("reader is closed");

        if(sqlite3_reset(this->cmd->stmt)!=SQLITE_OK)
            throw database_error(this->cmd->con);
    }

    void sqlite3_reader::close() {
        if(this->cmd) {
            if(--this->cmd->refs==0) sqlite3_reset(this->cmd->stmt);
            this->cmd=NULL;
        }
    }

    int sqlite3_reader::get_int(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return sqlite3_column_int(this->cmd->stmt, index);
    }

    long long sqlite3_reader::get_int64(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return sqlite3_column_int64(this->cmd->stmt, index);
    }

    double sqlite3_reader::get_double(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return sqlite3_column_double(this->cmd->stmt, index);
    }

    std::string sqlite3_reader::get_string(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return std::string((const char*)sqlite3_column_text(this->cmd->stmt, index), sqlite3_column_bytes(this->cmd->stmt, index));
    }

    std::wstring sqlite3_reader::get_string16(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return std::wstring((const wchar_t*)sqlite3_column_text16(this->cmd->stmt, index), sqlite3_column_bytes16(this->cmd->stmt, index)/2);
    }

    std::string sqlite3_reader::get_blob(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return std::string((const char*)sqlite3_column_blob(this->cmd->stmt, index), sqlite3_column_bytes(this->cmd->stmt, index));
    }

    std::string sqlite3_reader::get_colname(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return sqlite3_column_name(this->cmd->stmt, index);
    }

    std::wstring sqlite3_reader::get_colname16(int index) {
        if(!this->cmd) throw database_error("reader is closed");
        if((index)>(this->cmd->argc-1)) throw std::out_of_range("index out of range");
        return (const wchar_t*)sqlite3_column_name16(this->cmd->stmt, index);
    }

    int sqlite3_reader::get_colindex( std::string colname )
    {
        colname_index_t::iterator iter = colnames->find(colname);
        if( iter != colnames->end() )
            return iter->second;

        if(!this->cmd) throw database_error("reader is closed");
        for( int index = 0; index < this->cmd->argc; index++ ){
            if( get_colname(index) == colname ){
                (*colnames)[colname] = index;
                return index;
            }
        }

        throw std::out_of_range("colname is not in the result");
    }

    //int sqlite3_reader::get_colindex( std::wstring colname )
    //{
    //    if(!this->cmd) throw database_error("reader is closed");
    //    for( int index = 0; index < this->cmd->argc; index++ )
    //        if( get_colname16(index) == colname )
    //            return index;

    //    throw std::out_of_range("colname is not in the result");
    //}

    int sqlite3_reader::get_int( std::string colname )
    {
        return get_int(get_colindex(colname));
    }

    long long sqlite3_reader::get_int64( std::string colname )
    {
        return get_int64(get_colindex(colname));
    }

    double sqlite3_reader::get_double( std::string colname )
    {
        return get_double(get_colindex(colname));
    }

    std::string sqlite3_reader::get_string( std::string colname )
    {
        return get_string(get_colindex(colname));
    }

    std::wstring sqlite3_reader::get_string16( std::string colname )
    {
        return get_string16(get_colindex(colname));
    }

    std::string sqlite3_reader::get_blob( std::string colname )
    {
        return get_blob(get_colindex(colname));
    }
}
